<?php


declare(strict_types=1);

namespace app\eslib;

/**
 * 加密助手类
 * @version 1.0.0
 */
class EsEncrypt
{

    /**
     * 混淆参数值
     * <br />轻量混淆，支持中文，兼顾性能、安全与编码问题
     * <br />Base64-encoded 数据要比原始数据多占用 33% 左右的空间。
     * @param string $text 原文字符串
     * @param string $timestamp 时间戳
     */
    public static function base64Mixed($text, $timestamp = '')
    {
        // Unix时间戳由数值类型转换成字符串类型
        $timestamp = strval($timestamp);
        // 在原文前面增加时间戳，这样同样的内容在不同的时间生成的混淆字符串会一直变化，让人肉眼无法辨识
        $text = $timestamp . $text;
        // base64编码，替换base64的默认补位符号，让人无法轻易识破是base64编码
        $hash = strtr(base64_encode($text), '+/=', '-_.');
        $lenHash = strlen($hash);
        $len1 = intval(floor($lenHash / 2));
        $len2 = intval(ceil($lenHash / 2));
        // 把base64编码后的内容从中间拆分成前后两部分，并前后互换位置
        return substr($hash, $len1) . substr($hash, 0, $len2);
    }
    /**
     * 反混淆
     * @param string $hash 混淆的字符串
     * @param string $timestamp 时间戳
     */
    public static function base64DeMixed($hash, $timestamp = '')
    {
        // 混淆时间戳后，最小混淆字符串长度是16位
        $lenHash = strlen($hash);
        if ($lenHash < 16) {
            return $hash;
        }
        $len1 = intval(floor($lenHash / 2));
        $len2 = intval(ceil($lenHash / 2));
        $hash = substr($hash, $len1) . substr($hash, 0, $len2);
        $hash = strtr($hash, '-_.', '+/=');
        $text = base64_decode($hash);
        $lenTimestamp = strlen(strval($timestamp));
        return $lenTimestamp > 0 ? substr($text, $lenTimestamp) : $text;
    }
    /**
     * 生成参数签名
     * @param array &$params 请求参数数组
     * @param integer $appid 应用ID
     * @param string $appkey 应用KEY
     * @return string 返回sign参数签名字符串
     */
    public static function makeParamSignature(&$params, $appid, $appkey)
    {
        // 过滤空数组
        $params = array_filter($params);
        // 加入时间戳参数
        $params['timestamp'] = time();
        // 加入应用ID参数
        $params['appid'] = $appid;
        // 加入应用Key参数
        $params['appkey'] = $appkey;
        // 加入随机值参数
        $params['nonce'] = substr(uniqid(), 7);
        // 数组按键值正序重新排序
        ksort($params);
        // 用md5加密重新串联成请求字符串的参数数组
        $sign = md5(http_build_query($params));
        // 截取中间16位作为签名
        $sign = substr($sign, 8, 16);
        // 删除appkey参数
        unset($params['appkey']);
        // 加入签名参数
        $params['sign'] = $sign;
        return $sign;
    }
    /**
     * 验证参数签名
     * @param array $params 请求参数数组，一般为 appid,nonce,sign,timestamp 四个就可以了
     * @return array 
     */
    public static function validateParamSignature($params, $appkey)
    {
        if (!is_array($params)) {
            return ['status' => false, 'message' => '签名校验失败：请求参数错误'];
        }
        $needKeys = ['timestamp', 'appid', 'sign', 'nonce'];
        foreach ($needKeys as $key) {
            if (empty($params[$key])) {
                return ['status' => false, 'message' => '签名校验失败：请求参数无效'];
            }
        }
        array_filter($params);
        extract($params);
        // 链接请求1分钟内使用有效
        $invideTimeStamp = time() - 360;
        if ($timestamp < $invideTimeStamp) {
            return ['status' => false, 'message' => '签名校验失败：请求过期失效'];
        }
        if ($appid == '-1') {
            $appkey = 'a99AE2d2a736X65f5Ye63Ae299b0e339';
        }
        if (!$appkey) {
            return ['status' => false, 'message' => '签名校验失败：应用未注册'];
        }
        unset($params['sign']);
        $params['appkey'] = $appkey;
        ksort($params);
        $servSign = substr(md5(http_build_query($params)), 8, 16);
        if ($sign != $servSign) {
            return ['status' => false, 'message' => '签名校验失败：签名无效 ' . $servSign];
        }
        return ['status' => true, 'message' => '签名校验成功：签名有效'];
    }
    /**
     * 密码加密（不可逆）
     */
    public static function hashPassword($password, $salt = '')
    {
        // 截取中间16位作为签名
        return substr(md5($password . $salt), 8, 16);
    }

    /**
     * 获取不重复序列号（不可逆）
     * 大约是原来长度的一半,比如12位生成6位,21位生成13位
     */
    public static function hashSerial($prefix = '')
    {
        $time = date('y-m-d-h-i-s');
        if (is_numeric($prefix)) {
            $time = chunk_split(strval($prefix), 2, '-') . $time;
            $prefix = '';
        }
        $atime = explode('-', $time);
        foreach ($atime as $stime) {
            $itime = $stime * 1;
            if ($itime < 26) {
                $prefix .= chr(65 + $itime);
                continue;
            }
            if ($itime >= 48 && $itime <= 57) {
                $prefix .= chr($itime);
                continue;
            }
            $prefix .= $stime;
        }
        return $prefix;
    }
}
